/**
 * TManRankingTest.java
 * ************************** 
 * @date May 5, 2011
 * @author Michele Tamburini
 * 
 * **************************
 */
package JUnitTests;


import static org.junit.Assert.*;
import gossipServices.basic.nodeDescriptors.ConcreteNodeDescriptor;
import gossipServices.basic.nodeDescriptors.NodeDescriptor;
import gossipServices.basic.nodeDescriptors.comparators.IdSorterer;
import gossipServices.basic.view.ConcretePartialView;
import gossipServices.basic.view.PartialView;
import gossipServices.bootstraping.tman.TManRankingMethod;
import gossipServices.bootstraping.tman.rankingMethods.GeneralRankingMethod;
import gossipServices.bootstraping.tman.rankingMethods.RankingPair;
import gossipServices.bootstraping.tman.rankingMethods.SortedRing;
import gossipServices.bootstraping.tman.rankingMethods.rankingComparators.RankSorter;

import java.util.Collections;
import java.util.List;
import java.util.Vector;

import org.junit.Before;
import org.junit.Test;

/**
 * Description: 
 *
 */
public class TManRankingTest extends TestUtilFunctions {
	
	private static Vector<NodeDescriptor> desc;
	private static ConcretePartialView view;
//	private static String names[] = {"node1", "node2", "node3", "node4",
//		"topolino", "pippo", "pluto", "minnie", "gabibbo", "balanzone"};
	private static String names[] = {"node1", "node3", "node4", "node6", "node5","node7", "node2"};
	private String allNames[] = {"node1", "node2", "node3", "node4", "node5","node6", "node7"};
	
	private TManRankingMethod rankingMethod;
	private String baseNodeName = "node4";
	private NodeDescriptor baseNode;
	
	/**
	 * @throws java.lang.Exception
	 */
	@Before
	public void setUp() throws Exception {
		desc = new Vector<NodeDescriptor>();
		for(int i=0; i<names.length; i++){
			desc.add(new ConcreteNodeDescriptor(names[i]));
		}
		
		view = new ConcretePartialView(desc);
	}
	
	@Test
	public void testRing(){
		List<RankingPair> list;
		int k = 5;
		rankingMethod = new SortedRing(k);
		baseNode = new ConcreteNodeDescriptor(baseNodeName);
		String expectedOrderNames[] = {"node3", "node5", "node2", "node6", "node1", "node7"};
		Vector<NodeDescriptor> expectedOrder = new Vector<NodeDescriptor>();
		for(String s : expectedOrderNames)
			expectedOrder.add(new ConcreteNodeDescriptor(s));
		
		print("test: SORTED RING\n");
		print("baseNode: "+baseNode);
		print("view before:\n"+view);
		
		PartialView retView = rankingMethod.rank(baseNode, view);
		// the returned view is inserted in a new object
		assertTrue(view != retView);
		assertEquals(k, retView.size());
		assertTrue(!(
				((ConcretePartialView) retView).getDescriptors().contains(baseNode)));
		
		list =((GeneralRankingMethod)rankingMethod).getPairsList();
		print("ranked view:\n"+list);
		print("retView:\n"+retView);
		for(int i=0; i< retView.size(); i++){
			assertEquals(expectedOrder.get(i), retView.getAt(i));
		}
		
		k=2;
		rankingMethod = new SortedRing(k);
		retView = rankingMethod.rank(baseNode, view);
		assertEquals(k, retView.size());
		print("Using a different SortedRing with k= "+k+"\n"+retView);
		
		// trying overwrite the existing view:
		view = (ConcretePartialView) rankingMethod.rank(baseNode, view);
		assertEquals(k, retView.size());
		print("Overwrite the existing view test:\n"+view);
		assertTrue(view != retView);
		for(int i=0; i<retView.size(); i++){
			assertTrue(view.getAt(i).equals(view.getAt(i)));
		}
	}
	
	@Test
	public void distanceTest(){
		int k = 5;
		List<RankingPair> list;
		rankingMethod = new SortedRing(k);
		baseNode = new ConcreteNodeDescriptor(baseNodeName);
		//***************** DISTANCE TEST *************************
		print("distance test:");
		print("baseNode: "+baseNode);
		String names2[] = {"node7", "node3", "node1", "node5",
			"albatros", "pippo", "pluto", "minnie", "gabibbo"};
		desc.clear();
		for(String s : names2){
			desc.add(new ConcreteNodeDescriptor(s));
		}
		view = new ConcretePartialView(desc);
		print("before:\n"+view);
		PartialView retView2 = rankingMethod.rank(baseNode, view);
		print("pairs:\n"+((GeneralRankingMethod)rankingMethod).getPairsList());
		print("after:\n"+retView2);
		
		Integer targetID = baseNode.getNodeID();
		double distance, precDistance =0;
		double abs;
		for(int i =0; i<retView2.size(); i++){
			abs = Math.abs(targetID - retView2.getAt(i).getNodeID());
//			distance = Math.min( abs,( Math.pow(2, NodeDescriptor.ID_LENGTH) - abs ));
			distance = abs;
			print(i+") distance: "+distance);
			assertTrue(precDistance <= distance);
			precDistance = distance;
		}
		
	}
	@Test
	public void rotationTest(){
		int k = 5;
		List<RankingPair> list;
		rankingMethod = new SortedRing(k);
		baseNode = new ConcreteNodeDescriptor(baseNodeName);
		//*********************************************************
		//*************** ROTATION TEST ***************************
		print("\nrotation test");
		PartialView retView3;
		String names3[] = {"node4", "node3", "node6", "node5","node7", "node2"};
		//building:
		desc.clear();
		desc = buildVector(allNames);
		
		//*********************************************************
		// baseNode = "node1"
		//*********************************************************
		String baseNodeString = "node1";
		baseNode = new ConcreteNodeDescriptor(baseNodeString);
		desc.remove(baseNode);
		view = new ConcretePartialView(desc);
		String expectedOrderNames3[] = {"node2", "node7", "node3", "node6", "node4", "node5"};
		Vector<NodeDescriptor> expectedOrder3 = buildVector(expectedOrderNames3);
		
				
		print("baseNode: "+baseNode);
		print("before:\n"+view);
		retView3 = rankingMethod.rank(baseNode, view);
		list = ((GeneralRankingMethod)rankingMethod).getPairsList();
		print("after:\n"+list);
		
		assertEquals(k, retView3.size());
		for(int i=0; i<retView3.size(); i++){
			assertEquals(retView3.getAt(i), expectedOrder3.get(i));
		}

		//*********************************************************
		// baseNode = "node2"
		//*********************************************************
		baseNodeName = "node2";
		baseNode = new ConcreteNodeDescriptor(baseNodeName);
		String expectedOrderNames3_1[] = {"node1", "node3", "node4", "node7", "node5", "node6"};
		expectedOrder3.clear();
		expectedOrder3 = buildVector(expectedOrderNames3_1);
		desc.clear();
		desc = buildVector(allNames);
		desc.remove(baseNode);
		view = new ConcretePartialView(desc);
		
		print("baseNode: "+baseNode);
		print("before:\n"+view);
		retView3 = rankingMethod.rank(baseNode, view);
		print("after:\n"+retView3);
		
		assertEquals(k, retView3.size());
		for(int i=0; i<retView3.size(); i++){
			assertEquals(retView3.getAt(i), expectedOrder3.get(i));
		}
		
		//*********************************************************
		// baseNode = "node7"
		//*********************************************************
		baseNodeString = "node7";
		baseNode = new ConcreteNodeDescriptor(baseNodeString);
		String expectedOrderNode7[] = {"node1", "node6", "node2", "node5", "node3", "node4"};
		doTheJob(baseNodeString, expectedOrderNode7 );
		
		//*********************************************************
		// baseNode = "node4"
		//*********************************************************
		baseNodeString = "node4";
		baseNode = new ConcreteNodeDescriptor(baseNodeString);
		String expectedOrderNode4[] = {"node3", "node5", "node2", "node6", "node1", "node7"};
		doTheJob(baseNodeString, expectedOrderNode4 );
		
		//*********************************************************
		// baseNode = "node3"
		//*********************************************************
		baseNodeString = "node3";
		baseNode = new ConcreteNodeDescriptor(baseNodeString);
		String expectedOrderNode3[] = {"node2", "node4", "node1", "node5", "node6", "node7"};
		doTheJob(baseNodeString, expectedOrderNode3 );
		
		//*********************************************************
		// baseNode = "node5"
		//*********************************************************
		baseNodeString = "node5";
		baseNode = new ConcreteNodeDescriptor(baseNodeString);
		String expectedOrderNode5[] = {"node4", "node6", "node3", "node7", "node1", "node2"};
		doTheJob(baseNodeString, expectedOrderNode5 );
		
		//*********************************************************
		// baseNode = "node6"
		//*********************************************************
		baseNodeString = "node6";
		baseNode = new ConcreteNodeDescriptor(baseNodeString);
		String expectedOrderNode6[] = {"node5", "node7", "node1", "node4", "node2", "node3"};
		doTheJob(baseNodeString, expectedOrderNode6 );
		
		
	}
	
	@Test
	public void baseNodeRemoval(){
		int k = 5;
		List<RankingPair> list;
		rankingMethod = new SortedRing(k);
		baseNode = new ConcreteNodeDescriptor(baseNodeName);
		//*************** BASE NODE REMOVAL ***********************
		print("BaseNode Removal");
		List<NodeDescriptor> maliciousAdder = new Vector<NodeDescriptor>();
		maliciousAdder.add(new ConcreteNodeDescriptor(baseNode));
		maliciousAdder.add(new ConcreteNodeDescriptor(baseNode));
		maliciousAdder.add(new ConcreteNodeDescriptor(baseNode));
		PartialView maliciousView = new ConcretePartialView(maliciousAdder);
		if( !view.append(maliciousView) ){
			print("Problem in the append of the view");
		}
		
		print("Before the ranking the view is:\n"+view);
		PartialView retView = rankingMethod.rank(baseNode, view);
		Vector<ConcreteNodeDescriptor> vec = ((ConcretePartialView) retView).getDescriptors();
		print("Resulting View:\n"+retView);
		assertTrue(vec.contains(baseNode) == false);
		
		Vector<ConcreteNodeDescriptor> oldView = view.getDescriptors();
		Vector<ConcreteNodeDescriptor> rankedView = view.getDescriptors();
		
		for(ConcreteNodeDescriptor node : oldView){
			if(!node.equals(baseNode)){
				assertTrue(rankedView.contains(node));
			}
		}
		
		// View composed of only baseNode will return an empty view!
		PartialView funnyView = new ConcretePartialView(0);
		int EXAGERATE_VIEW_SIZE = 56;
		for(int i=0; i<EXAGERATE_VIEW_SIZE; i++)
			funnyView.insertNodeDescriptor(baseNode);
		PartialView retFunnierView = rankingMethod.rank(baseNode, funnyView);
		assertEquals(0, retFunnierView.size());
		
		
	}
	
	@Test
	public void sameResultWithDifferentInputs(){
		List<RankingPair> list;
		baseNode = new ConcreteNodeDescriptor(baseNodeName);
		String expectedOrderNames[] = {"node3", "node5", "node2", "node6", "node1", "node7"};
		int k = expectedOrderNames.length;
		rankingMethod = new SortedRing(k);
		Vector<NodeDescriptor> expectedOrderVec = buildVector(expectedOrderNames);
		Vector<NodeDescriptor> myInitilaizationVector;
		PartialView retView;
		
		// *****************************************************
		int MAX_NAMES = 7;
		String[][] names = new String[MAX_NAMES][]; 
		names[0] = new String[]{"node7", "node5", "node2", "node3", "node6", "node1", "node4"};
		names[1] = new String[]{"node4", "node7", "node5", "node2", "node3", "node6", "node1"};
		names[2] = new String[]{"node1", "node6", "node7", "node3", "node5", "node4", "node2"};
		names[3] = new String[]{"node2", "node1", "node6", "node7", "node3", "node4", "node5"};
		names[4] = new String[]{"node5", "node3", "node2", "node1", "node7", "node4", "node6"};
		names[5] = new String[]{"node6", "node4", "node3", "node5", "node1", "node2", "node7"};
		int TOTAL = 5;
		
		print("Same Output Result with different input views of shuffled names" +
				"TEST:");
		for(int i=0; i<=TOTAL; i++){
			print("test: "+i);
			myInitilaizationVector = buildVector(names[i]);
			view = new ConcretePartialView(myInitilaizationVector);
			print("input view:\n"+view);
			retView = rankingMethod.rank(baseNode, view);
			print("retView:\n"+retView);
			for(int j=0; j<expectedOrderVec.size(); j++){
				assertEquals(expectedOrderVec.get(j), retView.getAt(j));
			}
		}
		
	}
	
	@Test
	public void highValuesTest(){
//		String names[] = {"node7", "node5","node37" , "node2", "node3", "node6", "node1", "node4", "node10",
//				"node11", "node19", "node21", "node26", "node28", "node32", "node35", "node41",
//				"node56", "node78", "node55"};
		String names[] = { "node21##", "node11##", "node18##", "node3###", "node22##", "node5###", 
				"node33##", "node2###", "node34##", "node23##", "node35##", "node12##", "node40##",
				"node13"};
		baseNode = new ConcreteNodeDescriptor(baseNodeName);
		Vector<NodeDescriptor> vector = buildVector(names);
		PartialView retView;
		view = new ConcretePartialView(vector);
		rankingMethod = new SortedRing(names.length);
		retView = rankingMethod.rank(baseNode, view);
		List<RankingPair> pairList = new Vector<RankingPair>();
		
		Collections.sort(vector, new IdSorterer());
		for(NodeDescriptor n : vector){
			pairList.add(new RankingPair(n, 0));
		}
		Collections.sort(pairList, new RankSorter());
		
		print("HIGH VALUES TEST:");
		print("Starting view:\n"+view);
		print("Base Node: "+baseNode);
		print("Returning view:\n"+retView);
		
		// we have to proceed by pairs of nodes
		int precDistance[] = new int[2], actualDistance[] = new int[2];
		int baseNodeID = baseNode.getNodeID().intValue();
		int abs = Math.abs( baseNodeID - retView.getAt(0).getNodeID().intValue());
		precDistance[0] = Math.min( Integer.MAX_VALUE - abs, abs  );
		precDistance[1] = Math.min( Integer.MAX_VALUE - abs, abs  );

//		for(int i=1; i<names.length; i++){
//			abs = Math.abs( baseNodeID - retView.getAt(i).getNodeID().intValue() );
//			actualDistance[(i%2)] = Math.min( Integer.MAX_VALUE- abs, abs) ;
//			print("testing: "+ i+") precDistance: "+precDistance[i%2]+" actualDistance:"
//					+actualDistance[i%2]);
//			assertTrue(precDistance[i%2] <= actualDistance[i%2]);
//			precDistance[i%2] = actualDistance[i%2];
//		}
		
		for(int i=0; i< retView.size(); i++){
			print(i+")");
			assertEquals(vector.get(i), retView.getAt(i));
		}
	}
	
	private void doTheJob(String baseName, String expectedOrder[]){
		Vector<NodeDescriptor> expectedOrderVec;
		baseNode = new ConcreteNodeDescriptor(baseName);
		expectedOrderVec = buildVector(expectedOrder);
		desc.clear();
		desc = buildVector(allNames);
		desc.remove(baseNode);
		view = new ConcretePartialView(desc);
		print("baseNode: "+baseNode);
		print("before:\n"+view);
		PartialView retView = rankingMethod.rank(baseNode, view);
		print("after:\n"+retView);
		
		for(int i=0; i<retView.size(); i++){
			assertEquals(expectedOrderVec.get(i),retView.getAt(i) );
		}
	}

}
